/**
 ******************************************************************************
 * @file app_artmaster.c
 * @author GX (2524913364@qq.com)
 * @brief  绘画app
 *         (1) 有画笔大小、橡皮擦和画笔颜色可以选择。
 *         (2) 画笔绘画时每个点是画圆的形式，橡皮擦擦除时是以正方形的方式擦除。
 * @version 1.0.0 
 * @date 2022-07-12
 * 
 * @copyright Copyright (c) 2022
 * 
 ******************************************************************************
 *----------------------------------------------------------------------------*
 *  Remark         : Description                                              *
 *----------------------------------------------------------------------------*
 *  Change History :                                                          *
 *  <Date>     | <Version> | <Author>       | <Description>                   *
 *----------------------------------------------------------------------------*
 *  2022-07-12 | 1.0.0     | GX             | Create file                     *
 *----------------------------------------------------------------------------*
 *                                                                            *
 ******************************************************************************
 */


/*----------------------------- start of file -------------------------------*/

#include "app_artmaster.h"
#include "lcd.h"
#include "touch_screen.h"
 

/*------------------------ Static function declaration ------------------------*/

static void init_DrawingBoard(int *fb_base, int fd, 
                              struct fb_var_screeninfo *vinfo);
static ART_KEY art_getkey(int x, int y);
static void art_circular(int *fb_base, int center_x, int center_y, 
                              int R, int color);
static void art_rectangle(int *fb_base, int x, int y, 
                              int wide ,int height, int color);


/*------------------------ Variable function declaration -----------------------*/
int pen_size = ART_BRUSH_S_SIZE;
int pen_color = ART_COLOR_BLACK;


/*******************************************************************************
 * 
 *                               绘画APP主要函数
 * 
*******************************************************************************/

/**
 * @brief 绘画APP
 * 
 * @return true 
 * @return false 
 */
bool app_DrawingBoard(void)
{
  printf("into artmaster\n");
    /*------------------------------开启显示屏幕-----------------------------*/
  int fd_lcd;                             /* 打开文件获取文件描述 */
  int *fb_base = NULL;                    /* 映射后显存的地址 */
  struct fb_var_screeninfo vinfo;         /* 显卡设备的可变属性结构体 */
	/* 打开文件 */
	fd_lcd = open(LCD_PATH, O_RDWR);
	if(fd_lcd == -1)
	{
		perror("open lcd");
    return false;
	}
  else
	{
		/* 显存映射 */
		fb_base = mmap(NULL, LCD_BUFF_SIZE*LCD_BLOCK,  PROT_READ|PROT_WRITE,
					MAP_SHARED, fd_lcd, 0);
		if(fb_base == MAP_FAILED) 
		{
			perror("申请显存失败");
      close(fd_lcd);
      return false;
		} 

    if(ioctl(fd_lcd, FBIOGET_VSCREENINFO, &vinfo) != 0)/* 获取可变属性 */
    {
        perror("获取LCD设备可变属性信息失败");
        return false;
    }
  }

  /*--------------------------------正式开始相册相关-----------------------------*/
  bool back = false;
  init_DrawingBoard(fb_base, fd_lcd, &vinfo);


  /* 更新状态机 */
  Renew_tcState();
  TOUCH *tc = malloc(sizeof(TOUCH));
  ART_KEY key = ART_KEY_NONE;

  bool get_pressed = false;   /* 第一次按住时的坐标不取，(调试过程中的经验) */
  while(1)
  {
    get_tcState(tc);
    if( tc->response )
    {
      continue;
    }

    switch (tc->state)
    {
    case STATE_SINGLE_CLICK:  /* 单击 */
      response_over();
      key = art_getkey(tc->x_last, tc->y_last);
      if( key == ART_KEY_BACK )
      {
        back = true;
      }
      get_pressed = false;
      printf("key:%d\n", key);
      break;

    case STATE_DTAGING:       /* 拖动 */
      // response_over();
      if(pen_color != ART_COLOR_ERASER)
      {
        art_circular(LCD_DISPLAY(fb_base, view_block), 
                    tc->x_last, tc->y_last, pen_size, pen_color);
      }
      else
      {
        art_rectangle(LCD_DISPLAY(fb_base, view_block), 
                        (tc->x_last - pen_size), (tc->y_last - pen_size),
                            pen_size*2, pen_size*2, pen_color);
      }
      get_pressed = false;
      break;

    case STATE_PRESSED:       /* 按住,操作与拖动一样 */
      // response_over();
      if(( tc->x_last != 0 )&&( tc->y_last != 0 ))
      { 
        get_pressed = true;
        if(pen_color != ART_COLOR_ERASER)
        {
          art_circular(LCD_DISPLAY(fb_base, view_block), 
                      tc->x_last, tc->y_last, pen_size, pen_color);
        }
        else
        {
          art_rectangle(LCD_DISPLAY(fb_base, view_block), 
                          (tc->x_last - pen_size), (tc->y_last - pen_size),
                              pen_size*2, pen_size*2, pen_color);
        }
      }
      break;

    default:
      response_over();
      get_pressed = false;
      break;
    }

    if( back )
    {
      break;
    }
  }
  /* while(1) end--------------------------------------------------------- */

  view_block = 0;
  // free(tc);
  munmap(fb_base, LCD_BUFF_SIZE*LCD_BLOCK);
  close(fd_lcd);

  printf("out artmaster\n");
  return true;
} 


/*******************************************************************************
 * 
 *                               绘画APP辅助函数
 * 
*******************************************************************************/

/**
 * @brief 初始化画板
 * 
 * @param fb_base 显存映射的基地址
 * @param fd      显示屏的文件描述符
 * @param vinfo   显示屏可变属性的指针
 */
static void init_DrawingBoard(int *fb_base, int fd, 
                              struct fb_var_screeninfo *vinfo)
{
  view_block++;
  /* 显示画板 */
  lcd_show_bmp(LCD_DISPLAY(fb_base, view_block), ART_BOARD_PATH, 0, 0);

  /* 显示画笔大小选择按键 */
  lcd_circular(LCD_DISPLAY(fb_base, view_block),          /* 显存映射地址 */
                  ART_BMINI_X_CENTER, ART_BMINI_Y_CENTER, /* 圆心坐标 */
                      ART_BRUSH_MINI_SIZE, LCD_BLACK);    /* 半径和颜色 */
  
  lcd_circular(LCD_DISPLAY(fb_base, view_block),          
                  ART_S_X_CENTER, ART_S_Y_CENTER, 
                      ART_BRUSH_S_SIZE, LCD_BLACK);
  
  lcd_circular(LCD_DISPLAY(fb_base, view_block), 
                  ART_M_X_CENTER, ART_M_Y_CENTER, 
                      ART_BRUSH_M_SIZE, LCD_BLACK);
  
  lcd_circular(LCD_DISPLAY(fb_base, view_block), 
                  ART_L_X_CENTER, ART_L_Y_CENTER, 
                      ART_BRUSH_L_SIZE, LCD_BLACK);                                

  /* 显示色板 */
  lcd_rectangle(LCD_DISPLAY(fb_base, view_block),
                  ART_RED_X_START, ART_RED_Y_START,
                      ART_COLOR_WIDTH, ART_COLOR_HEIGH, ART_COLOR_RED);
  
  lcd_rectangle(LCD_DISPLAY(fb_base, view_block),
                  ART_GREEN_X_START, ART_GREEN_Y_START,
                      ART_COLOR_WIDTH, ART_COLOR_HEIGH, ART_COLOR_GREEN);
  
  lcd_rectangle(LCD_DISPLAY(fb_base, view_block),
                  ART_BLUE_X_START, ART_BLUE_Y_START,
                      ART_COLOR_WIDTH, ART_COLOR_HEIGH, ART_COLOR_BLUE);
  
  lcd_rectangle(LCD_DISPLAY(fb_base, view_block),
                  ART_BLACK_X_START, ART_BLACK_Y_START,
                      ART_COLOR_WIDTH, ART_COLOR_HEIGH, ART_COLOR_BLACK);                                            

  
  LCD_SET_DISPLAY(fd, vinfo, view_block);

  return;
}                 

/**
 * @brief 获取键值
 * 
 * @param x 点击的x坐标
 * @param y 点击的y坐标
 * @return ART_KEY 
 */
static ART_KEY art_getkey(int x, int y)
{
  /* 选择迷你笔 */
  if( (x > ART_BMINI_X_START) && (x < ART_BMINI_X_END)
      && (y > ART_BMINI_Y_START) && (y < ART_BMINI_Y_END))
  {
    pen_size = ART_BRUSH_MINI_SIZE;
    return ART_KEY_MINI;
  }

  /* 选择小号笔 */
  if( (x > ART_S_X_START) && (x < ART_S_X_END)
      && (y > ART_S_Y_START) && (y < ART_S_Y_END))
  {
    pen_size = ART_BRUSH_S_SIZE;
    return ART_KEY_S;
  }
  /* 选择中号笔 */
  if( (x > ART_M_X_START) && (x < ART_M_X_END)
      && (y > ART_M_Y_START) && (y < ART_M_Y_END))
  {
    pen_size = ART_BRUSH_M_SIZE;
    return ART_KEY_M;
  }

  /* 选择大号笔 */
  if( (x > ART_L_X_START) && (x < ART_L_X_END)
      && (y > ART_L_Y_START) && (y < ART_L_Y_END))
  {
    pen_size = ART_BRUSH_L_SIZE;
    return ART_KEY_L;
  }

  /* 选择红色 */
  if( (x > ART_RED_X_START) && (x < ART_RED_X_END)
      && (y > ART_RED_Y_START) && (y < ART_RED_Y_END))
  {
    pen_color = ART_COLOR_RED;
    return ART_KEY_RED;
  }

  /* 选择绿色 */
  if( (x > ART_GREEN_X_START) && (x < ART_GREEN_X_END)
      && (y > ART_GREEN_Y_START) && (y < ART_GREEN_Y_END))
  {
    pen_color = ART_COLOR_GREEN;
    return ART_KEY_GREEN;
  }

  /* 选择蓝色 */
  if( (x > ART_BLUE_X_START) && (x < ART_BLUE_X_END)
      && (y > ART_BLUE_Y_START) && (y < ART_BLUE_Y_END))
  {
    pen_color = ART_COLOR_BLUE;
    return ART_KEY_BLUE;
  }

  /* 选择黑色 */
  if( (x > ART_BLACK_X_START) && (x < ART_BLACK_X_END)
      && (y > ART_BLACK_Y_START) && (y < ART_BLACK_Y_END))
  {
    pen_color = ART_COLOR_BLACK;
    return ART_KEY_BLACK;
  }

  /* 选择橡皮擦 */
  if( (x > ART_ERASER_X_START) && (x < ART_ERASER_X_END)
      && (y > ART_ERASER_Y_START) && (y < ART_ERASER_Y_END))
  {
    pen_color = ART_COLOR_ERASER;
    return ART_KEY_ERASER;
  }

  /* 退出 */
  if( (x > ART_BACK_X_START) && (x < ART_BACK_X_END)
      && (y > ART_BACK_Y_START) && (y < ART_BACK_Y_END))
  {
    return ART_KEY_BACK;
  }

  printf("key none\n");
  return ART_KEY_NONE;
}

/**
 * @brief 绘制实心圆，从”lcd.h“中复制过来修改成适合该功能的需求
 * 
 * @param fb_base 	屏幕文件显存映射的基地址
 * @param center_x 	圆心的 x 坐标
 * @param center_y 	圆心的 y 坐标
 * @param R 				半径大小
 * @param color			圆形颜色,颜色在头文件 flags.h 中有定义好了常用颜色可以选择，当然
 * 		用户也可以根据自己的喜好设置颜色。每个像素的颜色由四个字节数据组成，每个字节分
 * 		别代表：透明度、红、绿、蓝。
 * 			@ref Common_colors	(in flag.h)	
 */
static void art_circular(int *fb_base, int center_x, int center_y, int R, int color)
{
	int r = 0;									
	int line_start, row_start;	/* 起始行列 */
	int line_end, row_end;			/* 结束行列 */

	/* 根据圆心和半径确定一个矩形区域，在这个区域中画圆 */
	line_start = ( center_y - R > ART_BOARD_Y_START ) ? 
                  ( center_y - R ) : ART_BOARD_Y_START;
	row_start  = ( center_x - R > ART_BOARD_X_START ) ? 
                  ( center_x - R ) : ART_BOARD_X_START;

	line_end = ( center_y + R < ART_BOARD_Y_END ) ? 
                  ( center_y + R ) : ART_BOARD_Y_END;
	row_end  = ( center_x + R < ART_BOARD_X_END ) ? 
                  ( center_x + R ) : ART_BOARD_X_END;

	for ( int h = line_start; h < line_end; h++ )
	{
		for ( int w = row_start; w < row_end; w++ )
		{
			r = ((w-center_x) * (w-center_x) + (h-center_y) * (h-center_y));
			if( r <= R*R )
				*(fb_base + ROW*h + w) = color;
		}
	}

}

/**
 * @brief 绘制一个实心矩形，从”lcd.h“中复制过来修改成适合该功能的需求
 * 
 * @param fb_base 屏幕文件显存映射的基地址
 * @param x 			矩形的起始 x 坐标
 * 			@arg	0 ~ LCD_WIDE
 * @param y 			矩形的起始 y 坐标
 * 			@arg  0 ~ LCD_HEIGHT
 * @param wide 		希望绘制的矩形宽度，为了防止超出内存范围取值范围根据输入的起
 * 		始 x 坐标而变化。
 * 			@arg  0 ~ (LCD_WIDE - x)
 * @param height  希望绘制的矩形高度，为了防止超出内存范围取值范围根据输入的起
 * 		始 y 坐标而变化。
 * 			@arg  0 ~ (LCD_HEIGHT - y)
 * @param color 	矩形颜色,颜色在头文件 flags.h 中有定义好了常用颜色可以选择，当然
 * 		用户也可以根据自己的喜好设置颜色。每个像素的颜色由四个字节数据组成，每个字节分
 * 		别代表：透明度、红、绿、蓝。
 * 			@ref Common_colors	(in flag.h)	
 */
static void art_rectangle(int *fb_base, int x, int y, int wide ,int height, int color)
{
	/* 起始坐标不在合理范围内就直接退出 */
	if( x < ART_BOARD_X_START || x > ART_BOARD_X_END)
	{
		printf("x is not within the reasonable value range\n");
		return;
	}
	if ( y < ART_BOARD_Y_START || y > ART_BOARD_Y_END)
	{
		printf("x is not within the reasonable value range\n");
		return;
	}
	
	/* 确保矩形的长和宽在合理取值范围内 */
	wide = ( x+wide > ART_BOARD_X_END ) ? ( ART_BOARD_X_END - x ) : ( wide ); 
	height = ( y+height > ART_BOARD_Y_END ) ? ( ART_BOARD_Y_END - y ) : ( height );

	/* 开始绘制 */
  for(int h = y; h < ( y+height ); h++)	/* 行 */
	{
		for(int w = x; w < ( x+wide ); w++)			/* 列 */
		{
			*( fb_base + ( h*LCD_WIDE+w ) ) = color;
		}
	}

	return;
}


/*------------------------------ end of file --------------------------------*/

